package com.ccys.common.third.aws.s3;


import com.ccys.common.third.aws.s3.model.S3Credentials;

import com.ccys.common.util.StringUtil;
import lombok.extern.slf4j.Slf4j;

import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.ResponseBytes;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.core.waiters.WaiterResponse;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest;
import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;
import software.amazon.awssdk.services.s3.waiters.S3Waiter;
import software.amazon.awssdk.services.sts.StsClient;
import software.amazon.awssdk.services.sts.model.AssumeRoleRequest;
import software.amazon.awssdk.services.sts.model.AssumeRoleResponse;
import software.amazon.awssdk.services.sts.model.Credentials;
import software.amazon.awssdk.services.sts.model.StsException;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.*;
import software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener;


import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.net.URL;

import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;


/**
 * 亚马逊 S3存储
 * <p>
 * 参考在线文档：https://docs.aws.amazon.com/AmazonS3/latest/userguide/setting-up-s3.html
 * S3官方文档：https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html
 * https://blog.csdn.net/Gefangenes/article/details/130719006
 * https://docs.aws.amazon.com/sdk-for-java/
 *
 * 2.0 版本文档https://docs.aws.amazon.com/zh_cn/sdk-for-java/latest/developer-guide/home.html
 *https://docs.aws.amazon.com/zh_cn/sdk-for-java/latest/developer-guide/examples-s3-buckets.html
 *
 * 获取最新版 https://mvnrepository.com/artifact/software.amazon.awssdk/s3
 * @author Administrator
 * S3 SDK代码 https://github.com/awsdocs/aws-doc-sdk-examples/blob/2ee04a54e839b5cb15b69a1be6f1e824de604320/javav2/example_code/s3/src/main/java/com/example/s3/S3BucketOps.java
 *
 * https://github.com/awsdocs/aws-doc-sdk-examples/blob/b1949eadb8097ab0d5b797f3f5a0625c6ede238c/javav2/example_code/s3/src/main/java/com/example/s3/S3ObjectOperations.java
 */
@Slf4j
@Component
@DependsOn({"s3Constants"})
public class S3Util {

    public static S3Client s3ClientV2;
    public static S3TransferManager s3TransferManager;
    public static String accessKey = "AKIAIHBBQGPKQR7KO5BQ";
    public static String secretKey = "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";
    public static String region;
    /**
     * 默认桶
     */
    public static String bucketName = "prv.staging.whitecoat.global";




    //初始化2.X版本SDK
    static {
        // 替换为你的S3桶所在的地区 "ap-southeast-1"
        System.setProperty("aws.region", Region.AP_SOUTHEAST_1.toString());
        if(StringUtil.isNotEmpty(S3Constants.region)) {
            if(Region.US_WEST_1.toString().equals(S3Constants.region)) {
                System.setProperty("aws.region", Region.US_WEST_1.toString());
            }
        }
        if(StringUtil.isNotEmpty(S3Constants.accessKeyId)) {
            S3Util.accessKey = S3Constants.accessKeyId;
        }
        if(StringUtil.isNotEmpty(S3Constants.accessKeySecret)) {
            S3Util.secretKey = S3Constants.accessKeySecret;
        }
        System.setProperty("aws.accessKeyId", S3Util.accessKey);
        System.setProperty("aws.secretAccessKey", S3Util.secretKey);
        //Region.US_EAST_1;
        Region region = Region.AP_SOUTHEAST_1;
        S3Client s3 = S3Client.builder()
            .region(region)
            //启用跨区域访问  2.20.111 及以上版本
            .crossRegionAccessEnabled(true)
            .build();

        s3ClientV2 = s3;
    }

    //初始化2.X版本S3TransferManager  SDK
    //  https://docs.aws.amazon.com/zh_cn/sdk-for-java/latest/developer-guide/transfer-manager.html
   /* static {
        S3AsyncClient s3AsyncClient = S3AsyncClient.crtBuilder()
            .credentialsProvider(DefaultCredentialsProvider.create())
            .region(Region.US_EAST_1)
            .targetThroughputInGbps(20.0)
            .minimumPartSizeInBytes(8*1024*1024*60L)
            .build();

        S3TransferManager transferManager = S3TransferManager.builder()
            .s3Client(s3AsyncClient)
            .build();

        s3TransferManager = transferManager;
    }*/



       /**
        * @description 创建存储桶列表  Create a bucket by using a S3Waiter object
        * @date     2024/2/21
        * @author   cloud fly
        */

    public static void createBucket( S3Client s3Client, String bucketName) {
        try {
            S3Waiter s3Waiter = s3Client.waiter();
            CreateBucketRequest bucketRequest = CreateBucketRequest.builder()
                .bucket(bucketName)
                .build();

            s3Client.createBucket(bucketRequest);
            HeadBucketRequest bucketRequestWait = HeadBucketRequest.builder()
                .bucket(bucketName)
                .build();

            // Wait until the bucket is created and print out the response.
            WaiterResponse<HeadBucketResponse> waiterResponse = s3Waiter.waitUntilBucketExists(bucketRequestWait);
            waiterResponse.matched().response().ifPresent(System.out::println);
            System.out.println(bucketName +" is ready");

        } catch (S3Exception e) {
            System.err.println(e.awsErrorDetails().errorMessage());

        }
    }




       /**
        * @description 为上传生成预签名 URL  Create a presigned URL to use in a subsequent PUT request
        * @date     2024/2/22
        * @author   cloud fly
        */
    public static String createPresignedUrl(String bucketName, String keyName, Map<String, String> metadata) {
        if(StringUtil.isEmpty(bucketName)) {
            bucketName = S3Util.bucketName;
        }
        if(StringUtil.isEmpty(keyName)) {
            return null;
        }
        try (S3Presigner presigner = S3Presigner.create()) {

            PutObjectRequest objectRequest = PutObjectRequest.builder()
                .bucket(bucketName)
                .key(keyName)
                .metadata(metadata)
                .build();

            PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder()
                .signatureDuration(Duration.ofMinutes(10))  // The URL expires in 10 minutes.
                .putObjectRequest(objectRequest)
                .build();


            PresignedPutObjectRequest presignedRequest = presigner.presignPutObject(presignRequest);
            String myURL = presignedRequest.url().toString();
            log.info("Presigned URL to upload a file to: [{}]", myURL);
            log.info("HTTP method: [{}]", presignedRequest.httpRequest().method());

            return presignedRequest.url().toExternalForm();
        }
    }


       /**
        * @description 为对象生成预签名下载 URL  Create a pre-signed URL to download an object in a subsequent GET request.
        * @date     2024/2/22
        * @author   cloud fly
        */
    public static String createPresignedGetUrl(String bucketName, String keyName) {
        if(StringUtil.isEmpty(bucketName)) {
            bucketName = S3Util.bucketName;
        }
        if(StringUtil.isEmpty(keyName)) {
            return null;
        }
        try (S3Presigner presigner = S3Presigner.create()) {

            GetObjectRequest objectRequest = GetObjectRequest.builder()
                .bucket(bucketName)
                .key(keyName)
                .build();

            GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder()
                .signatureDuration(Duration.ofMinutes(10))  // The URL will expire in 10 minutes.
                .getObjectRequest(objectRequest)
                .build();

            PresignedGetObjectRequest presignedRequest = presigner.presignGetObject(presignRequest);
            log.info("Presigned URL: [{}]", presignedRequest.url().toString());
            log.info("HTTP method: [{}]", presignedRequest.httpRequest().method());

            return presignedRequest.url().toExternalForm();
        }
    }
    public static String createPresignedGetUrl(String keyName) {
        return createPresignedGetUrl("",keyName);
    }


       /**
        * @description 使用httpUrl 上传文件(测试成功)
        * @date     2024/2/22
        * @author   cloud fly
        */
    public static void useHttpUrlConnectionToPut(String presignedUrlString, File fileToPut, Map<String, String> metadata) {
        if(fileToPut == null || StringUtil.isEmpty(presignedUrlString)) {
            return;
        }
        log.info("Begin [{}] upload", fileToPut.toString());
//        System.setProperty("proxyHost", "127.0.0.1");
//        System.setProperty("proxyPort", "17890");
        try {
            URL presignedUrl = new URL(presignedUrlString);
            HttpsURLConnection connection = (HttpsURLConnection) presignedUrl.openConnection();
            connection.setDoOutput(true);
            //metadata.forEach((k, v) -> connection.setRequestProperty("x-amz-meta-" + k, v));
            connection.setRequestMethod("PUT");
            OutputStream out = connection.getOutputStream();

            try (RandomAccessFile file = new RandomAccessFile(fileToPut, "r");
                 FileChannel inChannel = file.getChannel()) {
                ByteBuffer buffer = ByteBuffer.allocate(8192); //Buffer size is 8k

                while (inChannel.read(buffer) > 0) {
                    buffer.flip();
                    for (int i = 0; i < buffer.limit(); i++) {
                        out.write(buffer.get());
                    }
                    buffer.clear();
                }
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }

            out.close();
            connection.getResponseCode();
            log.info("HTTP response code is " + connection.getResponseCode());

        } catch (S3Exception | IOException e) {
            log.error(e.getMessage(), e);
        }


    }



       /**
        * @description 获取存储桶列表
        * @date     2024/2/21
        * @author   cloud fly
        */
    public  static void getListBuckets() {
        // List buckets
        ListBucketsRequest listBucketsRequest = ListBucketsRequest.builder().build();
        ListBucketsResponse listBucketsResponse = s3ClientV2.listBuckets(listBucketsRequest);
        listBucketsResponse.buckets().stream().forEach(x -> System.out.println("桶名称="+x.name()));
    }

    // The AWS IAM Identity Center identity (user) who executes this method does not have permission to list buckets.
    // The identity is configured in the [default] profile.
       /**
        * @description  AWS STS 为 IAM 代入的角色返回的临时凭证创建 Amazon S3 服务客户端
        * @date     2024/2/21
        * @author   cloud fly
        * 参考 https://docs.aws.amazon.com/zh_cn/sdk-for-java/latest/developer-guide/credentials-explicit.html
        */
    public static S3Credentials getAssumeRoleSTS(String roleArn, String roleSessionName) {
        // The IAM role represented by the 'roleArn' parameter can be assumed by identities in two different accounts
        // and the role permits the user to only list buckets.

        // The SDK's default credentials provider chain will find the single sign-on settings in the [default] profile.
        // The identity configured with the [default] profile needs permission to call AssumeRole on the STS service.
        S3Credentials s3Credentials = new S3Credentials();
        if(StringUtil.isEmpty(roleArn) || StringUtil.isEmpty(roleSessionName)) {
            return s3Credentials;
        }
        try {
            Credentials tempRoleCredentials;
            try (StsClient stsClient = StsClient.create()) {
                AssumeRoleRequest roleRequest = AssumeRoleRequest.builder()
                    .roleArn(roleArn)
                    .roleSessionName(roleSessionName)
                    .build();

                AssumeRoleResponse roleResponse = stsClient.assumeRole(roleRequest);
                tempRoleCredentials = roleResponse.credentials();
            }
            // Use the following temporary credential items for the S3 client.
            String key = tempRoleCredentials.accessKeyId();
            String secKey = tempRoleCredentials.secretAccessKey();
            String secToken = tempRoleCredentials.sessionToken();
            Instant expiration = tempRoleCredentials.expiration();



            s3Credentials.setAccessKeyId(key);
            s3Credentials.setAccessKeySecret(secKey);
            s3Credentials.setSecurityToken(secToken);
            s3Credentials.setExpiration(String.valueOf(expiration.toEpochMilli()));
            // List all buckets in the account associated with the assumed role
            // by using the temporary credentials retrieved by invoking stsClient.assumeRole().
            //测试后应该 可去掉的验证代码
            StaticCredentialsProvider staticCredentialsProvider = StaticCredentialsProvider.create(
                AwsSessionCredentials.create(key, secKey, secToken));
            try (S3Client s3 = S3Client.builder()
                .credentialsProvider(staticCredentialsProvider)
                .build()) {
                List<Bucket> buckets = s3.listBuckets().buckets();
                for (Bucket bucket : buckets) {
                    System.out.println("bucket name: " + bucket.name());
                }
            }
        } catch (StsException | S3Exception e) {
            log.info(e.getMessage());
        }
        return  s3Credentials;
    }



    // This example uses RequestBody.fromFile to avoid loading the whole file into memory.
       /**
        * @description 上传文件到S3 桶
        * @param bucketName The Amazon S3 bucket to upload an object into.
        * @param objectKey The object to upload (for example, book.pdf).
        * @param objectPath The path where the file is located (for example, C:/AWS/book2.pdf).
        * @date     2024/2/21
        * @author   cloud fly
        */
    public static String putS3Object( String bucketName, String objectKey, String objectPath) {
        if(StringUtil.isEmpty(bucketName)) {
            bucketName = S3Util.bucketName;
        }
        if(StringUtil.isEmpty(objectKey) || StringUtil.isEmpty(objectPath)) {
            return null;
        }
        try {
            Map<String, String> metadata = new HashMap<>();
            metadata.put("x-amz-meta-myVal", "test");
            PutObjectRequest putOb = PutObjectRequest.builder()
                .bucket(bucketName)
                .key(objectKey)
                .metadata(metadata)
                .build();

            s3ClientV2.putObject(putOb, RequestBody.fromFile(new File(objectPath)));
            System.out.println("Successfully placed " + objectKey + " into bucket " + bucketName);

        } catch (S3Exception e) {
            System.err.println(e.getMessage());

        }
        return S3Util.getURL(S3Util.bucketName, objectKey);
    }



    /**
     * @description 上传文件到S3 桶
     * @param bucketName The Amazon S3 bucket to upload an object into.
     * @param objectKey The object to upload (for example, book.pdf).
     * @param inputStream 流
     * @date     2024/2/21
     * @author   cloud fly
     */
    public static String putS3ObjectByInputStream( String bucketName, String objectKey, InputStream inputStream) {
        if(StringUtil.isEmpty(bucketName)) {
            bucketName = S3Util.bucketName;
        }
        if(inputStream == null) {
            return null;
        }
        try {
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            int nRead;
            byte[] data = new byte[1024];
            while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
                buffer.write(data, 0, nRead);
            }
            buffer.flush();
            byte[] byteArray = buffer.toByteArray();
            Map<String, String> metadata = new HashMap<>();
            metadata.put("x-amz-meta-myVal", "test");
            PutObjectRequest putOb = PutObjectRequest.builder()
                .bucket(bucketName)
                .key(objectKey)
                .metadata(metadata)
                .build();
            //直接上传流  byteArray.length  上传S3 会连接失败
            //s3ClientV2.putObject(putOb, RequestBody.fromInputStream(inputStream,length));
            //流转字节上传 Test success
            s3ClientV2.putObject(putOb, RequestBody.fromBytes(byteArray));
            System.out.println("Successfully placed " + objectKey + " into bucket " + bucketName);

        } catch (Exception e) {
            System.err.println(e.getMessage());

        }
        return S3Util.getURL(S3Util.bucketName, objectKey);
    }

    /**
     * @description 上传文件到S3 桶
     * @param bucketName The Amazon S3 bucket to upload an object into.
     * @param objectKey The object to upload (for example, book.pdf).
     * @param bytes 字节流
     * @date     2024/2/21
     * @author   cloud fly  /doctor/123/  image.jpg
     */
    public static String putS3ObjectByByte( String bucketName, String objectKey, byte[] bytes) {
        if(StringUtil.isEmpty(bucketName)) {
            bucketName = S3Util.bucketName;
        }
        if(bytes == null) {
            return null;
        }
        try {

            Map<String, String> metadata = new HashMap<>();
            metadata.put("x-amz-meta-myVal", "test");
            PutObjectRequest putOb = PutObjectRequest.builder()
                .bucket(bucketName)
                .key(objectKey)
                .metadata(metadata)
                .acl(ObjectCannedACL.PUBLIC_READ)
                .build();

            PutObjectResponse putObjectResponse = s3ClientV2.putObject(putOb, RequestBody.fromBytes(bytes));
            System.out.println("Successfully placed " + objectKey + " into bucket " + bucketName);

        } catch (Exception e) {
            System.err.println(e.getMessage());

        }
        return S3Util.getURL(S3Util.bucketName, objectKey);
    }

       /**
        * @description 从S3 获取文件字节流 并写入到path中
        * @param bucketName 桶名
        * @param keyName 文件名称
        * @param path 下载位置
        * @date     2024/2/21
        * @author   cloud fly
        */
    public static void getObjectBytes(String bucketName, String keyName, String path) {
        if(StringUtil.isEmpty(bucketName)) {
            bucketName = S3Util.bucketName;
        }
        if(StringUtil.isEmpty(keyName) || StringUtil.isEmpty(path)) {
            return;
        }
        try {
            GetObjectRequest objectRequest = GetObjectRequest
                .builder()
                .key(keyName)
                .bucket(bucketName)
                .build();

            ResponseBytes<GetObjectResponse> objectBytes = s3ClientV2.getObjectAsBytes(objectRequest);
            byte[] data = objectBytes.asByteArray();

            // Write the data to a local file.
            File myFile = new File(path);
            OutputStream os = new FileOutputStream(myFile);
            os.write(data);
            System.out.println("Successfully obtained bytes from an S3 object");
            os.close();

        } catch (IOException ex) {
            ex.printStackTrace();
        } catch (S3Exception e) {
            System.err.println(e.awsErrorDetails().errorMessage());

        }
    }


    /**
     * @description 从S3 获取文件字节流
     * @param bucketName 桶名
     * @param keyName 文件名称
     * @return byte[] S3上的文件字节流
     * @date     2024/2/21
     * @author   cloud fly
     */
    public static byte[] getObjectBytes(String bucketName, String keyName) {
        if(StringUtil.isEmpty(bucketName)) {
            bucketName = S3Util.bucketName;
        }
        if(StringUtil.isEmpty(keyName)) {
            return null;
        }
        try {
            GetObjectRequest objectRequest = GetObjectRequest
                .builder()
                .key(keyName)
                .bucket(bucketName)
                .build();

            ResponseBytes<GetObjectResponse> objectBytes = s3ClientV2.getObjectAsBytes(objectRequest);
            byte[] data = objectBytes.asByteArray();

            System.out.println("Successfully obtained bytes from an S3 object");
            return data;

        } catch (S3Exception e) {
            System.err.println(e.awsErrorDetails().errorMessage());

        }
        return null;
    }


    /**
        * @description 使用 S3TransferManager 将文件上载到桶
        * @date     2024/2/21
        * @author   cloud fly
        */
    public String uploadFile(S3TransferManager transferManager, String bucketName,
                             String key, String filePath) {
        if(StringUtil.isEmpty(bucketName)) {
            bucketName = S3Util.bucketName;
        }
        String finalBucketName = bucketName;
        UploadFileRequest uploadFileRequest = UploadFileRequest.builder()
            .putObjectRequest(b -> b.bucket(finalBucketName).key(key))
            .addTransferListener(LoggingTransferListener.create())
            .source(Paths.get(filePath))
            .build();

        FileUpload fileUpload = transferManager.uploadFile(uploadFileRequest);

        CompletedFileUpload uploadResult = fileUpload.completionFuture().join();
        return uploadResult.response().eTag();
    }

       /**
        * @description 使用 S3TransferManager 将文件下载到桶
        * @date     2024/2/21
        * @author   cloud fly
        */
    public Long downloadFile(S3TransferManager transferManager, String bucketName,
                             String key, String downloadedFileWithPath) {
        if(StringUtil.isEmpty(bucketName)) {
            bucketName = S3Util.bucketName;
        }
        String finalBucketName = bucketName;
        DownloadFileRequest downloadFileRequest = DownloadFileRequest.builder()
            .getObjectRequest(b -> b.bucket(finalBucketName).key(key))
            .addTransferListener(LoggingTransferListener.create())
            .destination(Paths.get(downloadedFileWithPath))
            .build();

        FileDownload downloadFile = transferManager.downloadFile(downloadFileRequest);

        CompletedFileDownload downloadResult = downloadFile.completionFuture().join();
        log.info("Content length [{}]", downloadResult.response().contentLength());
        return downloadResult.response().contentLength();
    }

    private static ByteBuffer getRandomByteBuffer(int size)  {
        byte[] b = new byte[size];
        new Random().nextBytes(b);
        return ByteBuffer.wrap(b);
    }
    /**
     * Upload an object in parts
     */
    private static void multipartUpload(String bucketName, String key)  {
        if(StringUtil.isEmpty(bucketName)) {
            bucketName = S3Util.bucketName;
        }
        int mB = 1024 * 1024;
        // snippet-start:[s3.java2.s3_object_operations.upload_multi_part]
        // First create a multipart upload and get the upload id
        CreateMultipartUploadRequest createMultipartUploadRequest = CreateMultipartUploadRequest.builder()
            .bucket(bucketName)
            .key(key)
            .build();

        CreateMultipartUploadResponse response = s3ClientV2.createMultipartUpload(createMultipartUploadRequest);
        String uploadId = response.uploadId();
        System.out.println(uploadId);

        // Upload all the different parts of the object
        UploadPartRequest uploadPartRequest1 = UploadPartRequest.builder()
            .bucket(bucketName)
            .key(key)
            .uploadId(uploadId)
            .partNumber(1).build();

        String etag1 = s3ClientV2.uploadPart(uploadPartRequest1, RequestBody.fromByteBuffer(getRandomByteBuffer(5 * mB))).eTag();

        CompletedPart part1 = CompletedPart.builder().partNumber(1).eTag(etag1).build();

        UploadPartRequest uploadPartRequest2 = UploadPartRequest.builder().bucket(bucketName).key(key)
            .uploadId(uploadId)
            .partNumber(2).build();
        String etag2 = s3ClientV2.uploadPart(uploadPartRequest2, RequestBody.fromByteBuffer(getRandomByteBuffer(3 * mB))).eTag();
        CompletedPart part2 = CompletedPart.builder().partNumber(2).eTag(etag2).build();


        // Finally call completeMultipartUpload operation to tell S3 to merge all uploaded
        // parts and finish the multipart operation.
        CompletedMultipartUpload completedMultipartUpload = CompletedMultipartUpload.builder()
            .parts(part1, part2)
            .build();

        CompleteMultipartUploadRequest completeMultipartUploadRequest =
            CompleteMultipartUploadRequest.builder()
                .bucket(bucketName)
                .key(key)
                .uploadId(uploadId)
                .multipartUpload(completedMultipartUpload)
                .build();

        s3ClientV2.completeMultipartUpload(completeMultipartUploadRequest);
        // snippet-end:[s3.java2.s3_object_operations.upload_multi_part]
    }

       /**
        * @description 获取文件的存储路径
        * @param bucketName 桶名称
        * @param keyName 文件名
        * @return string 文件路径
        * @date     2024/2/23
        * @author   cloud fly
        */
    public static String getURL(String bucketName, String keyName) {
        if(StringUtil.isEmpty(keyName)) {
            return null;
        }
        if(StringUtil.isEmpty(bucketName)) {
            bucketName = S3Util.bucketName;
        }
        try {
            GetUrlRequest request = GetUrlRequest.builder()
                .bucket(bucketName)
                .key(keyName)
                .build();

            URL url = s3ClientV2.utilities().getUrl(request);
            System.out.println("The URL for  " + keyName + " is " + url);
            return url.toExternalForm();

        } catch (S3Exception e) {
            System.err.println(e.awsErrorDetails().errorMessage());
        }
        return null;
    }



}
